/*	Nama file		: Calendar.c
	Deskripsi		: Realisasi type dan prototype primitif type Calender dengan menggunakan Priority List linier berkait
	Nama pembuat	: Jonathan Ery Pradana (23512038), Muhammad Ghufron Mahfudhi (23512066), Ni Made Satvika (23512077), Karina Novita Suryani (23512187)
	Hari / Tanggal	: Kamis, 27 September 2012
*/

#include "Calendar.h"

/* Prototype manajemen memori */
void alokasiCal (addressCal *P, infoType X){
/* I.S. Sembarang */
/* F.S. Alamat P dialokasi, jika berhasil maka Info(P)=X dan Next(P)=NULL */
/* P=NULL jika alokasi gagal */
	/* Kamus Lokal */
	/* Algoritma */
	*P = (addressCal) malloc (sizeof(ElmtCal));
	if (*P != NULL) {	/* alokasi berhasil */
		Info(*P) = X;
		Next(*P) = NULL;
	}
}

void dealokasiCal (addressCal P){
/* I.S. P adalah hasil alokasi, P <> NULL */
/* F.S. Alamat P didealokasi, dikembalikan ke sistem */
	/* Kamus Lokal */
	/* Algoritma */
	free(P);
}

/*** Predikat Pemeriksaan Kondisi QueueCal ***/
int IsEmpty (ListCal C){
/* Mengirim 1 jika C kosong: First(C)=NULL */
	/* Kamus Lokal */
	/* Algoritma */
	return (First(C) == NULL);
}

int NBElmt(ListCal C){
/* Mengirimkan banyaknya elemen ListCal. Mengirimkan 0 jika C kosong */
	/* Kamus Lokal */
	addressCal p;
	int n;
	/* Algoritma */
	if(IsEmpty(C)){
		return 0;
	}
	else{
		p=First(C);
		n=0;
		do{
			n++;
			p=Next(p);
		} while(p!=NULL);
		return n;
	}
}

/*** Kreator ***/
void CreateEmpty(ListCal * C){
/* I.S. sembarang */
/* F.S. Sebuah C kosong terbentuk */
	/* Kamus Lokal */
	/* Algoritma */
	First(*C)=NULL;
}

/*** Destruktor ***/
void DelCal(ListCal * C){
/* I.S. C berisi */
/* F.S. C terhapus, setiap address nya didealokasi */
	/* Kamus Lokal */
	
	/* Algoritma */
	while(!IsEmpty(*C)){
		DelFirst(C);
	}
	CreateEmpty(C);
}

/*** Primitif Add/Delete ***/
void Add (ListCal * C, infoType X){
/* Proses: Mengalokasi X dan menambahkan X dalam C sesuai priority berdasarkan DateTime-nya jika alokasi berhasil; jika alokasi gagal C tetap */
/* I.S. C mungkin kosong */
/* F.S. X ditambahkan ke dalam C sesuai priority-nya */
	/* Kamus Lokal */
	addressCal P;
	addressCal tempAdr;
	/* Algoritma */
	alokasiCal(&P,X);
	if (P != NULL) {
		if (IsEmpty(*C) || IsDTBefore(GetDateTime(X),GetDateTime(InfoFirst(*C)))){
			Next(P) = First(*C);
			First(*C) = P;
		} 
		else {
			tempAdr = First(*C);
			while(Next(tempAdr)!=NULL && !IsDTAfter(GetDateTime(Info(Next(tempAdr))), GetDateTime(X))){
				tempAdr = Next(tempAdr);	
			}
			Next(P) = Next(tempAdr);
			Next(tempAdr) = P;
		}
		
	} /* else: alokasi gagal, Q tetap */
}

void DelFirst (ListCal * C){
/* Proses: Menghapus X pada bagian First dari C dan mendealokasi elemen First */
/* Pada dasarnya operasi delete first */
/* I.S. C tidak mungkin kosong */
/* F.S. X = nilai elemen First pd I.S., First "mundur" */
	/* Kamus Lokal */
	addressCal P;
	/* Algoritma */
	P = First(*C);
	dealokasiList(&(Info(P)));
	First(*C) = Next(First(*C));
	dealokasiCal(P);
}

void DelParam (ListCal * C, int ENum){
/* Proses: Menghapus Event dengan nomor ENum dari C dan mendealokasi elemen tersebut */
/* I.S. C tidak mungkin kosong */
/* F.S. X = nilai Event dengan nomor ENum, isi ListCal bergeser */
	/* Kamus Lokal */
	addressCal tempAdr;	//address index
	addressCal P;		//address yang akan dihapus
	/* Algoritma */
	
	//cek head
	if(GetEventNo(InfoFirst(*C)) == ENum){	//head == enum
		DelFirst(C);
	}
	else{	//head != enum
		tempAdr = First(*C);
		while(Next(tempAdr)!=NULL && GetEventNo(Info(Next(tempAdr)))!=ENum){
			tempAdr = Next(tempAdr);
		}
		
		if(Next(tempAdr)!=NULL && GetEventNo(Info(Next(tempAdr)))==ENum){
			P = Next(tempAdr);
			dealokasiList(&(Info(P)));
			Next(tempAdr) = Next(P);
			dealokasiCal(P);
			
		}// else, tidak mengubah list calendar
	}
}

/*** Primitif Tambahan ***/
addressCal SearchEvent(ListCal C, int ENum){
/* Mengirimkan address Event dengan nomor ENum di dalam Calender C */
	/* Kamus Lokal */
	addressCal tempAdr;
	/* Algoritma */
	if(IsEmpty(C)){
		return NULL;
	}
	else{
		tempAdr = First(C);
		while(tempAdr!=NULL && GetEventNo(Info(tempAdr))!=ENum){
			tempAdr = Next(tempAdr);
		}
		
		if(tempAdr!=NULL && GetEventNo(Info(tempAdr))==ENum){
			return tempAdr;
		}
		else{
			return NULL;
		}
	}
}

void Update(ListCal *C, int ENum){
/* Proses: Mengubah isi Event dengan nomor ENum */
/* I.S. C tidak mungkin kosong, Terdapat event dengan nomor Enum */
/* F.S. Event di dalam C dengan nomor ENum berganti atribut */

/* Log Kamis 4 Oktober : KarinaNS- Mengganti fungsi update menjadi delete dulu baru add*/
/* Log Minggu 7 Oktober : Karinans - Mengganti fungsi update dengan ada tambahan updateEvent*/

	/* Kamus Lokal */
	infoType tempEvent = Info(SearchEvent(*C,ENum));
	
	/* Algoritma */
	UpdateEvent(&tempEvent);
	DelParam (C, ENum);
	Add(C,tempEvent);
	
	printf("Event dengan nomor %d telah diedit\n",ENum);
	
}

void AddEvent(ListCal *C){
/* Proses: Membaca Event baru dan menambahkannya ke dalam kalender*/
/* I.S. C mungkin kosong */
/* F.S. Event baru ditambahkan ke dalam C */

/* Log Minggu 7 Oktober : Karinans - Membuat fungsi AddEvent dengan ada tambahan BacaEvent di dalamnya*/


	/* Kamus Lokal */
	infoType tempEvent;
	
	/* Algoritma */
	BacaEvent(&tempEvent);
	Add(C,tempEvent);
	printf("Event dengan nomor %d telah ditambahkan\n",GetEventNo(tempEvent));

}

/*** Primitif View ***/
void ViewEvent(ListCal C, int ENum){
/* Proses : Menampilkan event dengan nomor Enum di dalam C */
/* I.S. C mungkin kosong*/
/* F.S. Event dengan nomor Enum tampil di layar*/

/* Log Kamis 4 Oktober : Karinans - Menambahkan prosedur ViewEvent*/

	/* Kamus Lokal */
	
	/* Algoritma */
	if (SearchEvent(C, ENum) != NULL){
		TulisEvent(Info(SearchEvent(C, ENum)));
	}	else {
		printf("Event dengan nomor %d tidak ditemukan\n", ENum);
	}
}

void ViewNFirst(ListCal C, int N){
/* Proses: Menampilkan N event pertama di dalam C */
/* I.S. C tidak mungkin kosong */
/* F.S. Event sebanyak N pertama tampil di layar */

/* Log Minggu 7 Oktober : Karinans - Menyempurnakan prosedur*/


	/* Kamus Lokal */
	addressCal indexAdr;
	int i;

	/* Algoritma */
	indexAdr = First(C);
	i = 0;

	while(indexAdr!=NULL && i<N){
		printf("--------------------------------------------------\n");
		TulisEvent(Info(indexAdr));
		indexAdr = Next(indexAdr);
		i++;
	}

	printf("--------------------------------------------------\n");

}

void ViewNLast(ListCal C, int N){
/* Proses: Menampilkan N event terakhir di dalam C */
/* I.S. C mungkin kosong */
/* F.S. Event sebanyak N terakhir tampil di layar */

/* Log Minggu 7 Oktober : Karinans - Menyempurnakan prosedur*/

	/* Kamus Lokal */
	addressCal indexAdr;
	int i;

	/* Algoritma */
	indexAdr = First(C);
	i = 0;

	if(NBElmt(C)>N){
		while(indexAdr!=NULL && i<NBElmt(C)-N){
			indexAdr = Next(indexAdr);
			i++;
		}
	}

	i = 0;
	while(indexAdr!=NULL && i<N){
		printf("--------------------------------------------------\n");
		TulisEvent(Info(indexAdr));
		indexAdr = Next(indexAdr);
		i++;
	}

	printf("--------------------------------------------------\n");
			


}

void ViewListCal(ListCal C){
/* Proses: Menampilkan semua event di dalam C dalam bentuk list of event */
/* I.S. C mungkin kosong */
/* F.S. Semua event tampil di layar dalam bentuk list of event */

/*	LOG Minggu 7 Oktober 2012 : Jonathan Ery Membuat Fungsi */

	/* Kamus Lokal */
	addressCal indexAdr;
	int idArray[NBElmt(C)];
	DateTime dtArray[NBElmt(C)];
	int NBCal;
	int currentMonth;
	int currentYear;
	int dtI;
	int i;
	
	/* Algoritma */
	if(IsEmpty(C)){
		printf("Calender kosong\n");
	} else {
		//get two array of ID and DateTime
		indexAdr = First(C);
		for (i = 0;i<NBElmt(C);i++){
			idArray[i] = GetEventNo(Info(indexAdr));
			dtArray[i] = GetDateTime(Info(indexAdr));
			indexAdr = Next(indexAdr);
		}
		
		NBCal = NBMonth(dtArray,NBElmt(C));
		dtI = 0; //inisialisasi
		currentMonth = GetMonth(GetDate(dtArray[dtI])); //inisialisasi bulan pertama
		currentYear = GetYear(GetDate(dtArray[dtI])); //inisialisasi tahun pertama
		for (i = 0;i < NBCal;i++){
			if ((currentMonth != GetMonth(GetDate(dtArray[dtI]))) || (currentYear != GetYear(GetDate(dtArray[dtI])))){
				printf(">Bulan: %d - %d\n",currentMonth,currentYear);
				printf("Tidak ada Event Bulan ini\n");
			}else{
				printf(">Bulan: %d - %d\n",GetMonth(GetDate(dtArray[dtI])),GetYear(GetDate(dtArray[dtI])));
				printf("-----------------\n");
				printf("|Tanggal|EventID|\n");
				printf("-----------------\n");
				//printf("|%d\t|#%d\t|\n",GetDay(GetDate(dtArray[dtI])),idArray[dtI]);
				while ((dtI+1<NBElmt(C)) && (GetMonth(GetDate(dtArray[dtI+1])) == currentMonth) && (GetYear(GetDate(dtArray[dtI+1])) == currentYear)){
					if ((dtI-1>=0) && (IsEqD((GetDate(dtArray[dtI])),GetDate(dtArray[dtI-1])))){
						printf("|\t|#%d\t|\n",idArray[dtI]);
						if (!IsEqD((GetDate(dtArray[dtI])),GetDate(dtArray[dtI+1]))){
							printf("-----------------\n");
						}
					} else {
						printf("|%d\t|#%d\t|\n",GetDay(GetDate(dtArray[dtI])),idArray[dtI]);
						if (!IsEqD((GetDate(dtArray[dtI])),GetDate(dtArray[dtI+1]))){
							printf("-----------------\n");
						}
					}
					dtI++;
				}
				if ((GetMonth(GetDate(dtArray[dtI])) == currentMonth) && (GetYear(GetDate(dtArray[dtI])) == currentYear)){
					if ((dtI-1>=0) && (IsEqD((GetDate(dtArray[dtI])),GetDate(dtArray[dtI-1])))){
						printf("|\t|#%d\t|\n",idArray[dtI]);
					} else {
						printf("|%d\t|#%d\t|\n",GetDay(GetDate(dtArray[dtI])),idArray[dtI]);
					}
					printf("-----------------\n");
					dtI++;
				}
			}
			if (currentMonth == 12){
				currentYear++;
				currentMonth = 1;
			}else{
				currentMonth++;
			}
			printf("\n");
		}
	}
}

/** Primitif Read dan Save File untuk Elemen Kalender **/
void ReadFileCal (char* fileName, ListCal *C)
/* Proses : Membaca file dari fileName, kemudian menyimpan isinya ke C */ 
/* I.S : C telah dibuat, fileName terfdefinisi 	*/
/* F.S : C terisi data yang berasal dari fileName */

/* Log Kamis, 5 Oktober 2012 	: Membuat fileName dan C sebagai parameter prosedur */
/* Log Minggu, 7 Oktober 2012 	: Ni Made Satvika Iswari menambahkan penanganan format file yang salah */
{
	/* Kamus Lokal */
	int N;				// Jumlah event
	int i;				// Variabel iterasi
	char *CurString;	// Variabel sementara yang menyimpan string yang sedang dibaca
	boolean stop;		// Variabel yang menentukan apakah melanjutkan membaca file atau tidak
	FILE *F;			// File yang dibaca
	
	Event E;			// Variabel yang menyimpan event yang akan ditambahkan ke kalender	
	int entryNo;		// Nomor event
	char eventName[MAX_STRING];	// Nama Event
	Point place;		// Tempat Event
	DateTime dateTime;	// Tanggal dan Waktu Event
	ListStr nameList;	// List nama yang terlibat dalam event
	
	CurString = (char*) malloc (MAX_CHAR *sizeof (char));
	
	F = fopen(fileName, "r");
	if (F == NULL) {
		printf("File %s tidak dapat dibuka.\n", fileName);
	}else{
		if( fscanf (F, " %d", &N) != 1){
			printf("Format file salah. Gagal membaca file.\n");
		}else{
			/** Dilakukan pengecekan satu per satu terhadap masing-masing baris file. Jika tidak sesuai, maka pembacaan file dihentikan **/
			i = 0;
			stop = false;
			while (i < N && stop == false){
				/** Pembacaan nomor event **/
				if (fscanf (F, " %d", &entryNo) != 1){	
					printf("Format file salah. Gagal membaca file.\n");
					stop = true;
				}else{
					/** Pembacaan nama event **/
					if (fscanf (F, " %[^\n]s", eventName) != 1){
						printf("Format file salah. Gagal membaca file.\n");
						stop = true;
					}else{
						/** Pembacaan tempat event **/
						if (fscanf (F, " %[^\n]s", CurString) != 1){
							printf("Format file salah. Gagal membaca file.\n");
							stop = true;
						}else{
							place = MakePointfromStr(CurString);
							/** Pembacaan Tanggal dan Waktu **/
							if (fscanf (F, " %[^\n]s", CurString) != 1){
								printf("Format file salah. Gagal membaca file.\n");
								stop = true;
							}else{
								dateTime = MakeDateTimefromStr(CurString);
								/** Pembacaan daftar nama **/
								if (fscanf (F, " %[^\n]s", CurString) != 1){
									printf("Format file salah. Gagal membaca file.\n");
									stop = true;
								}else{
									nameList = MakeListfromStr(CurString);
									
									/** Membuat Event dari elemen - elemen yang sudah dibaca, kemudian menambahkannya ke kalender **/
									E = MakeEvent(entryNo, eventName, place, dateTime, nameList);
									Add(C, E);
									i++;
								}
							}
						}
					}
				}
			}
		}
		/* Menutup file */
		fclose(F);
	}
}

void SaveFileCal (char* fileName, ListCal C)
/* Proses : Menuliskan elemen - elemen C ke dalam File fileName */
/* I.S	: fileName terdefinisi, C berisi event yang akan dituliskan ke file */
/* F.S	: File yang bernama fileName terbentuk, dan berisi elemen C */

/* Log Kamis, 5 Oktober 2012 : Ni Made Satvika Iswari mengubah penulisan file dengan menggunakan PointToString, DateTimeToString, dan ListStrToString */
{
	/* Kamus Lokal */
	FILE *F;	// File yang akan dituliskan elemen C
	int i;		// Variabel sementara untuk iterasi
	addressCal indexAdr;	//alamat elemen C
	
	/* Algoritma */
	F = fopen(fileName, "w");
	if (F == NULL) {
		printf("File %s tidak dapat dibuka.\n", fileName);
	}else{
		fprintf(F, "%d\n", NBElmt(C));
		
		if (NBElmt(C) > 0){
			indexAdr = First(C);
			for (i = 0; i < NBElmt(C); ++i){
				fprintf(F, "%d\n", GetEventNo(Info(indexAdr)));		// Menuliskan nomor event
				fprintf(F, "%s\n", GetEventName(Info(indexAdr)));	// Menuliskan nama event
				fprintf(F, "%s\n", PointToString(GetPlace(Info(indexAdr))));	// Menuliskan tempat event
				fprintf(F, "%s\n", DateTimeToString(GetDateTime(Info(indexAdr))));	// Menuliskan Tanggal dan Waktu event
				fprintf(F, "%s\n", ListStrToString(GetNameList(Info(indexAdr))));	// Menuliskan Daftar Nama event
				
				indexAdr = Next(indexAdr);
			}
		}
		
		/* Menutup file */
		fclose(F);
	}
}
